﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace FourPartHarmony3 {
    class GrandStaffControl {
        HorizontalVirtualCanvas mScoreCanvas;
        ScoreElementPathes mPathes = new ScoreElementPathes();
        ChordRenderer mChordRenderer = new ChordRenderer();
        public enum StaffType {
            Realization,
            ChooseChord,
        }

        private StaffType mStaffType;
        List<Chord> mChordList = new List<Chord>();
        private int mSelectedChordIdx = -1;

        private MeterStruct mMeter = new MeterStruct(2, 2);

        private void AddGrandStaff(Vector pos, double width) {
            if (width <= Constants.MINIMUM_SCORE_WIDTH) {
                width = Constants.MINIMUM_SCORE_WIDTH;
            }

            for (int i = 0; i < 5; ++i) {
                // ト音記号の5線
                mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                    new Rect(
                        pos.X + Constants.LINE_LEFT,
                        pos.Y + Constants.LINE_G_TOP + i * Constants.LINE_TO_LINE_PIXEL,
                        width,
                        1)));
                // ヘ音記号の5線
                mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle, 
                    new Rect(
                        pos.X + Constants.LINE_LEFT,
                        pos.Y + Constants.LINE_F_TOP + i * Constants.LINE_TO_LINE_PIXEL,
                        width,
                        1)));
            }
            // bar(5線左端の小節線)
            mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                                new Rect(pos.X + Constants.LINE_LEFT, pos.Y + Constants.LINE_G_TOP,
                                         1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP)));
            // 大譜表くくりparen
            mScoreCanvas.AddVirtualChild(new VirtualScoreElement(ScoreElementPathes.PathItem(ScoreElementType.LARGE_PAREN),
                new Vector(
                    pos.X + Constants.LINE_LEFT,
                    pos.Y + Constants.LINE_G_TOP + (5 + (Constants.F_INTERVAL_LINES - 5) / 2) * Constants.LINE_TO_LINE_PIXEL)));

            // 譜面右端が見えるようにするための置物。
            AddPathToScore(ScoreElementType.DUMMY, new Vector(pos.X + width + Constants.LINE_TO_LINE_PIXEL*5, pos.Y));
        }

        public void Initialize(HorizontalVirtualCanvas scoreCanvas) {
            mScoreCanvas = scoreCanvas;
            ScoreElementPathes.Prepare();

            mChordRenderer.Initialize(mScoreCanvas);

            Draw();
        }

        private void AddPathToScore(ScoreElementType se, Vector pos) {
            mScoreCanvas.AddVirtualChild(new VirtualScoreElement(ScoreElementPathes.PathItem(se), pos));
        }

        public void Update(StaffType t, MeterStruct meter, List<Chord> chordList) {
            mChordList = chordList;
            mStaffType = t;
            mMeter = meter;
            Draw();
        }

        public void Draw() {
            mScoreCanvas.Clear();

            DrawChordList(mChordList,
                    new Vector(0, Constants.STAFF_OFFSET_Y));
        }

        private void DrawChordList(IEnumerable<Chord> chordEnum, Vector pos) {
            var ss = new StaffStatus(pos);
            ss.Meter = mMeter;
            if (mStaffType == StaffType.Realization) {
                ss.ShouldDrawMeter = true;
            }

            double lineStartX = pos.X + Constants.LINE_LEFT;

            Chord prevChord = null;
            foreach (var c in chordEnum) {
                switch (mStaffType) {
                case StaffType.Realization:
                    // 必要ならば小節線を追加する。
                    int prevBar = 0;
                    if (prevChord != null) {
                        prevBar = TimeTickInfo.TimeTickToBarNumber(ss.Meter, prevChord.TimeTick);
                    }
                    int nowBar = TimeTickInfo.TimeTickToBarNumber(ss.Meter, c.TimeTick);

                    if (nowBar != prevBar) {
                        // 新しい小節が始まった。

                        ss.AdvanceX(Constants.CHORD_END_TO_SEPARATOR_X);

                        mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                                new Rect(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_TOP,
                                         1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP + 1)));

                        // 臨時記号ストアをリセットする。
                        ss.ResetAccidentals();
                    }
                    break;
                }

                mChordRenderer.Draw(prevChord, c, ss);
                ss.CommitAccidentals();

                switch (mStaffType) {
                case StaffType.ChooseChord:
                    // 和音候補一覧は和音1個ずつセパレーター小節線をつける。
                    mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                            new Rect(ss.Pos.X,                                   ss.Pos.Y + Constants.LINE_G_TOP,
                                     1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP+1)));
                    mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                            new Rect(ss.Pos.X + Constants.DOUBLE_BAR_INTERVAL_X, ss.Pos.Y + Constants.LINE_G_TOP,
                                        1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP+1)));

                    ss.AdvanceX(Constants.DOUBLE_BAR_INTERVAL_X);

                    // 臨時記号ストアをリセットする。
                    ss.ResetAccidentals();
                    break;
                }
                prevChord = c;
            }

            mChordRenderer.DrawEnd(prevChord, ss);

            if (0 == chordEnum.Count()) {
                mSelectedChordIdx = -1;
            } else {
                if (mStaffType == StaffType.Realization) {
                    // 終わり小節線を追加する。
                    ss.AdvanceX(Constants.CHORD_END_TO_SEPARATOR_X + Constants.DOUBLE_BAR_INTERVAL_X);

                    mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                            new Rect(ss.Pos.X - Constants.DOUBLE_BAR_INTERVAL_X, ss.Pos.Y + Constants.LINE_G_TOP,
                                    1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP + 1)));
                    mScoreCanvas.AddVirtualChild(new VirtualShape(VirtualShape.Type.Rectangle,
                            new Rect(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_TOP,
                                    1, Constants.LINE_F_BOTTOM - Constants.LINE_G_TOP + 1)));

                }

                // 選択された和音にフォーカス枠をつける。
                if (mSelectedChordIdx == -1) {
                    mSelectedChordIdx = 0;
                }
                if (chordEnum.Count() <= mSelectedChordIdx) {
                    mSelectedChordIdx = chordEnum.Count() - 1;
                }
                mScoreCanvas.SetFocusAt(mSelectedChordIdx);
            }

            double lineWidth = ss.Pos.X - lineStartX;
            AddGrandStaff(new Vector(0, pos.Y), lineWidth);
        }

        public void OnTouch(Point xy) {
            int idx = mScoreCanvas.OnTouch(xy);
            if (0 <= idx) {
                mSelectedChordIdx = idx;
                if (ChooseChord != null) {
                    ChooseChord(GetSelectedChord());
                }
            }
        }

        public Chord GetSelectedChord() {
            if (mSelectedChordIdx < 0) {
                return null;
            }
            return mChordList[mSelectedChordIdx];
        }

        /// <summary>
        /// 選択中のChordの番号。
        /// </summary>
        /// <returns>1つもないときは-1。0番目の時は0。</returns>
        public int GetSelectedChordIdx() {
            return mSelectedChordIdx;
        }

        /// <summary>
        /// 選択中のChord番号を更新する。
        /// </summary>
        /// <param name="idx"></param>
        public void SetSelectedChordIdx(int idx) {
            if (idx < 0 || mChordList.Count <= idx) {
                return;
            }
            mSelectedChordIdx = idx;
            mScoreCanvas.SetFocusAt(mSelectedChordIdx);
        }

        public delegate void ChooseChordCallback(Chord c);

        public ChooseChordCallback ChooseChord { get; set; }
    }
}
